package com.gitee.wsl.math.interpolator

import androidx.compose.ui.graphics.Color
import com.gitee.wsl.mathematics.interpolator.Interpolate
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlin.math.pow
import kotlin.math.roundToInt

data class ColorInterpolationProgress(
    val progress: Float,
    val colors: List<Color>
)

fun Interpolate.Companion.colors(
    startColors: IntArray,
    endColors: IntArray
): Flow<ColorInterpolationProgress> = (0..100).asFlow()
    .onEach { delay(50) }
    .map { percentage ->
        val fraction = percentage.toFloat() / 100F
        val colors = startColors
            .zip(endColors)
            .map { (start, end) ->
                interpolateColors(
                    fraction = fraction,
                    startValue = start,
                    endValue = end
                )
            }
            .map(Int::toLong)
            .map(::Color)

        ColorInterpolationProgress(
            progress = fraction,
            colors = colors,
        )
    }

internal fun interpolateColors(fraction: Float, startValue: Int, endValue: Int): Int {
    val startA = (startValue shr 24 and 0xff) / 255.0f
    var startR = (startValue shr 16 and 0xff) / 255.0f
    var startG = (startValue shr 8 and 0xff) / 255.0f
    var startB = (startValue and 0xff) / 255.0f
    val endA = (endValue shr 24 and 0xff) / 255.0f
    var endR = (endValue shr 16 and 0xff) / 255.0f
    var endG = (endValue shr 8 and 0xff) / 255.0f
    var endB = (endValue and 0xff) / 255.0f

    // convert from sRGB to linear
    startR = startR.toDouble().pow(2.2).toFloat()
    startG = startG.toDouble().pow(2.2).toFloat()
    startB = startB.toDouble().pow(2.2).toFloat()
    endR = endR.toDouble().pow(2.2).toFloat()
    endG = endG.toDouble().pow(2.2).toFloat()
    endB = endB.toDouble().pow(2.2).toFloat()

    // compute the interpolated color in linear space
    var a = startA + fraction * (endA - startA)
    var r = startR + fraction * (endR - startR)
    var g = startG + fraction * (endG - startG)
    var b = startB + fraction * (endB - startB)

    // convert back to sRGB in the [0..255] range
    a *= 255.0f
    r = r.toDouble().pow(1.0 / 2.2).toFloat() * 255.0f
    g = g.toDouble().pow(1.0 / 2.2).toFloat() * 255.0f
    b = b.toDouble().pow(1.0 / 2.2).toFloat() * 255.0f

    return a.roundToInt() shl 24 or (r.roundToInt() shl 16) or (g.roundToInt() shl 8) or b.roundToInt()
}